package com.jiayou.tool

import com.jiayou.*
import com.jiayou.emnu.FrameType
import com.jiayou.emnu.User
import io.ktor.network.sockets.*
import io.ktor.utils.io.*
import java.io.*

/**
@author lishuang
@data   2020-11-19  16:32
@info   对象序列化工具
 */
//jvm对象序列化为字节数组
fun objectToByteArray(obj: Any): ByteArray {
    val byteArrayOutputStream = ByteArrayOutputStream()
    val objectOutputStream = ObjectOutputStream(byteArrayOutputStream)
    objectOutputStream.writeObject(obj)
    objectOutputStream.flush()
    return byteArrayOutputStream.toByteArray()
}

//将字节数组反序列化为Protocol对象
@SuppressWarnings("unused")
fun <T : Serializable, T1 : Serializable, T2 : Serializable> byteArrayToObject(byteArray: ByteArray): Protocol<T, T1, T2>? {
    val `in` = ByteArrayInputStream(byteArray)
    val sIn = ObjectInputStream(`in`)
    val protocol: Protocol<T, T1, T2>?
    protocol = try {
        sIn.readObject() as Protocol<T, T1, T2>?
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
    return protocol
}

fun <X> MutableList<Map<User, Any>>.getUser(intent: X) = when (intent) {
    is String -> this.find { (it[User.USERNAME]) == intent }
    is Socket -> this.find { (it[User.SOCKET]) === intent }
    is ByteReadChannel -> this.find { (it[User.READ_CHANNEL]) === intent }
    is ByteWriteChannel -> this.find { (it[User.WRITE_CHANNEL]) === intent }
    else -> null
}

fun <X> MutableList<Map<User, Any>>.getUsername(intent: X) = when (intent) {
    is Socket -> this.find { (it[User.SOCKET]) === intent }?.get(User.USERNAME)?.toUsername()
    is ByteReadChannel -> this.find { (it[User.READ_CHANNEL]) === intent }?.get(User.USERNAME)?.toUsername()
    is ByteWriteChannel -> this.find { (it[User.WRITE_CHANNEL]) === intent }?.get(User.USERNAME)?.toUsername()
    else -> null
}

fun <X> MutableList<Map<User, Any>>.getSocket(intent: X) = when (intent) {
    is String -> this.find { (it[User.USERNAME]) == intent }?.get(User.SOCKET)?.toSocket()
    is ByteReadChannel -> this.find { (it[User.READ_CHANNEL]) === intent }?.get(User.SOCKET)?.toSocket()
    is ByteWriteChannel -> this.find { (it[User.WRITE_CHANNEL]) === intent }?.get(User.SOCKET)?.toSocket()
    else -> null
}

fun <X> MutableList<Map<User, Any>>.getReadChannel(intent: X) = when (intent) {
    is String -> this.find { (it[User.USERNAME]) == intent }?.get(User.READ_CHANNEL)?.toReadChannel()
    is Socket -> this.find { (it[User.SOCKET]) === intent }?.get(User.READ_CHANNEL)?.toReadChannel()
    is ByteWriteChannel -> this.find { (it[User.WRITE_CHANNEL]) === intent }?.get(User.READ_CHANNEL)?.toReadChannel()
    else -> null
}

fun <X> MutableList<Map<User, Any>>.getWriteChannel(intent: X) = when (intent) {
    is String -> this.find { (it[User.USERNAME]) == intent }?.get(User.WRITE_CHANNEL)?.toWriteChannel()
    is Socket -> this.find { (it[User.SOCKET]) === intent }?.get(User.WRITE_CHANNEL)?.toWriteChannel()
    is ByteReadChannel -> this.find { (it[User.READ_CHANNEL]) === intent }?.get(User.WRITE_CHANNEL)?.toWriteChannel()
    else -> null
}

suspend fun MutableList<Map<User, Any>>.addUser(user: Map<User, Any>): Boolean {
    ktorLock.lock()
    return try {
        this.add(user)
    } catch (e: Exception) {
        e.printStackTrace()
        false
    } finally {
        ktorLock.unlock()
    }
}

suspend fun <X> MutableList<Map<User, Any>>.removeUser(intent: X): Boolean {
    ktorLock.lock()
    return try {
        when (intent) {
            is String -> this.removeIf { (it[User.USERNAME]) == intent }
            is Socket -> this.removeIf { (it[User.SOCKET]) == intent }
            is ByteReadChannel -> this.removeIf { (it[User.READ_CHANNEL]) == intent }
            else -> false
        }
    } catch (e: Exception) {
        e.printStackTrace()
        false
    } finally {
        ktorLock.unlock()
    }
}

suspend fun stateDispense(statePackage: StatePackage, targetUsers: Set<String>) {
    ktorLock.lock()
    try {
        val data = objectToByteArray(DefaultProtocolImpl(FrameType.ON_LINE, statePackage))
        ktor.forEach {
            if (it[User.SOCKET] in targetUsers) {
                val writerChannel = (it[User.WRITE_CHANNEL] ?: error("")).toWriteChannel()
                writerChannel.writeInt(data.size)
                writerChannel.writeFully(data)
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        ktorLock.unlock()
    }
}

suspend fun sendMessage(data: ByteArray, size: Int, socket: Socket) {
    ktorLock.lock()
    try {
        ktor.forEach {
            if (it[User.SOCKET] != socket) {
                val writerChannel = (it[User.WRITE_CHANNEL] ?: error("")).toWriteChannel()
                writerChannel.writeInt(size)
                writerChannel.writeFully(data)
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        ktorLock.unlock()
    }
}

suspend fun sendMessage(data: ByteArray, size: Int, condition: ((Map<User, Any>) -> Boolean)) {
    ktorLock.lock()
    try {
        ktor.forEach {
            if (predicate(condition, it)) {
                val writerChannel = (it[User.WRITE_CHANNEL] ?: error("")).toWriteChannel()
                writerChannel.writeInt(size)
                writerChannel.writeFully(data)
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        ktorLock.unlock()
    }
}

fun <T : (T1) -> Boolean, T1> predicate(function: T, data: T1) = function.invoke(data)

fun Any.toUsername() = this as String
fun Any.toSocket() = this as Socket
fun Any.toReadChannel() = this as ByteReadChannel
fun Any.toWriteChannel() = this as ByteWriteChannel